//
// Created by zerax on 2020/10/24.
//

#pragma once

#include <glad/glad.h>
#include <glm/glm.hpp>

#include <memory>
#include <string>
#include <map>


class ShaderManager;
class Shader;

struct Shader {
    friend ShaderManager;
public:
    ~Shader() {
        glDeleteProgram(m_rawHandle);
    }

    void use() const {
        glUseProgram(m_rawHandle);
    }

    GLint getUniformLocation(const char* name) const {
        return glGetUniformLocation(m_rawHandle, name);
    }

    void setUniform(const char* name, bool u) const {
        auto loc = getUniformLocation(name);
        glProgramUniform1i(m_rawHandle, loc, u);
    }

    void setUniform(const char* name, int u) const {
        auto loc = getUniformLocation(name);
        glProgramUniform1i(m_rawHandle, loc, u);
    }

    void setUniform(const char* name, unsigned int u) const {
        auto loc = getUniformLocation(name);
        glProgramUniform1ui(m_rawHandle, loc, u);
    }

    void setUniform(const char* name, float u) const {
        auto loc = getUniformLocation(name);
        glProgramUniform1f(m_rawHandle, loc, u);
    }

    void setUniform(const char* name, const glm::vec3& u) const {
        auto loc = getUniformLocation(name);
        glProgramUniform3fv(m_rawHandle, loc, 1, &u[0]);
    }

    void setUniform(const char* name, const glm::mat4& u) const {
        auto loc = getUniformLocation(name);
        glProgramUniformMatrix4fv(m_rawHandle, loc, 1, GL_FALSE, &u[0][0]);
    }

private:
    explicit Shader(int id): m_rawHandle(id) {}

private:
    GLuint m_rawHandle;
};

class ShaderManager {
public:
    static std::shared_ptr<ShaderManager> getInstance();

    void loadShader(const char* name, const char* vert, const char* frag);
    void loadShaderFromFile(const char* name, const char* vertPath, const char* fragPath);

    std::shared_ptr<Shader> getShader(const char* name);

private:
    ShaderManager();

private:
    std::map<std::string, std::shared_ptr<Shader>> shaders;
};





